from django import forms
from django.contrib import admin
from django.core.exceptions import PermissionDenied
from django.forms import ChoiceField, ValidationError
from django.forms.models import ModelChoiceField
from django.http import Http404, HttpResponseRedirect
from django.utils.html import escape
from django.utils.translation import ugettext as _
from django.conf.urls.defaults import patterns, url
from pagemagic.utils.ordering.admin import OrderingAdmin
try:
    from pagemagic import PAGEMAGIC_SELF_ADMIN_INDEX
except ImportError:
    PAGEMAGIC_SELF_ADMIN_INDEX = False
import models
import operator
#import re

#class PageAttrInlineAdmin(admin.TabularInline):
#    model = models.PageAttr
#    template = 'pagemagic/admin/edit_inline_page_attr.html'
#    max_num = len(models.page_attrs)
#    extra = max_num

class PageChoiceField(ModelChoiceField):
    def clean(self, value):
        ChoiceField.clean(self, value)
        try:
            value = models.Page.objects.get(pk=value)
        except models.Page.DoesNotExist: #@UndefinedVariable
            raise ValidationError('invalid_choice')
        return value


def enlarge_fields(form, fields):
    for fn in fields:
        form.base_fields[fn].widget.attrs = {'style': 'width:30em'}


#class PageAdmin(admin.ModelAdmin):
class PageAdmin(OrderingAdmin):
    change_form_template = 'pagemagic/admin/page_change_form.html'
    change_list_template = 'pagemagic/admin/page_change_list.html'
    save_on_top = True

    readonly_fields = ('get_native_page_name',)

    def get_form(self, request, obj=None, **kwargs):
        default_form = super(PageAdmin, self).get_form(request, obj, **kwargs)
        class form(default_form):
            def clean_path(self):
                path = self.cleaned_data['path']
                if path:
                    checkable_path = path.split('#', 1)[0]
                    if checkable_path and (not checkable_path.startswith('/') or not checkable_path.endswith('/')):
                        raise ValidationError(_('Path must be starts and ends with "/"'))
                return path

            def clean_name(self):
                name = self.cleaned_data['name']
                if name and name.startswith('/'):
                    # we select menu by path or name.
                    raise ValidationError(_('Name cannot starts with "/"'))
                return name

        def get_flat_children(item):
            return [item] + flatten_list(map(get_flat_children, item.get_children()))

        result = flatten_list(map(get_flat_children, models.Page.get_root_items()))
        form.base_fields['parent'].choices = [('', _('(root)'))] + [(item.pk, item.adm_menu_title()) for item in result]
        enlarge_fields(form, ['path'])

        return form

    def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
        context['parent'] = request.GET.get('parent')
        return super(PageAdmin, self).render_change_form(request, context, add, change, form_url, obj)

    def get_urls(self):
        urls = super(PageAdmin, self).get_urls()
        my_urls = patterns('',
#            url(
#                '^(?P<page_id>\d+)',
#                self.admin_site.admin_view(self.tessst),
#            ),
            url(
                '^(?P<page_id>\d+)/move-(?P<direction>up|down)',
                self.admin_site.admin_view(self.move_page),
            )
        )
        return my_urls + urls

    def tessst(self, request, page_id):
        raise SystemError('123 %s, %s' % (request.path, page_id))
#    def __call__(self, request, url):
#        raise ValueError(url)

#    def __call__(self, request, url):
#        ''' Overriden to route extra URLs.'''
#        if not hasattr(self, '_route'):
#            self._route = (
#                (re.compile('(?P<page_id>\d+)/move-(?P<direction>up|down)$'), self.move_page),
#                (re.compile('(?P<page_id>\d+)/move-(?P<direction>up|down)$'), self.move_page),
#            )
#
#        if request.path.endswith('/pagemagic/page/') and PAGEMAGIC_SELF_ADMIN_INDEX:
#            return HttpResponseRedirect('../../')
#
#        if url:
#            if url.endswith('/items'):
#                return HttpResponseRedirect('../')
#            for pattern, func in self._route:
#                m = pattern.match(url)
#                if m:
#                    return func(request, **m.groupdict())
#        return super(PageAdmin, self).__call__(request, url)

    def save_model(self, request, obj, form, change):
        cur_level_pages = self.model.objects.filter(parent=obj.parent).order_by('-order')
        if obj.order == 0 and len(cur_level_pages) > 0:
            obj.order = cur_level_pages[0].order + 1

        obj.save()

        cur_level_pages = self.model.objects.filter(parent=obj.parent).order_by('order')
        self.fix_orders(cur_level_pages)


    # warning! order is 1-based. if order == 0 => reorder
    def fix_orders(self, all_pages):
        #print list(all_pages)
        for i, page in enumerate(all_pages):
            if page.order != (i + 1):
                page.order = (i + 1)
                page.save()

    def move_page(self, request, direction, page_id):
        current_page = get_object_with_change_permissions(self, request, self.model, page_id)
        all_pages = self.model.objects.filter(parent=current_page.parent).order_by('order')
        if len(all_pages) > 1:
            self.fix_orders(all_pages)

            for page in all_pages:
                if page.pk == current_page.pk:
                    current_page = page
            # move
            if direction == 'up' and current_page.order != 1:
                swap_page_orders(current_page, all_pages[current_page.order - 2])
            if direction == 'down' and current_page.order != len(all_pages):
                swap_page_orders(current_page, all_pages[current_page.order])
        return HttpResponseRedirect('../../')

admin.site.register(models.Page, PageAdmin)


## UTILS ##
def swap_page_orders(page1, page2):
    page1.order, page2.order = page2.order, page1.order
    page1.save()
    page2.save()

def flatten_list(lst):
    return reduce(operator.add, lst, [])

def get_object_with_change_permissions(self, request, model, obj_pk):
    ''' Helper function that returns a menu/menuitem if it exists
    and if the user has the change permissions '''
    try:
        obj = model._default_manager.get(pk=obj_pk)
    except model.DoesNotExist:
        # Don't raise Http404 just yet, because we haven't checked
        # permissions yet. We don't want an unauthenticated user to be able
        # to determine whether a given object exists.
        obj = None
    if not self.has_change_permission(request, obj):
        raise PermissionDenied
    if obj is None:
        raise Http404('%s object with primary key %r does not exist.' % (model.__name__, escape(obj_pk)))
    return obj


class ExtensionForm(forms.ModelForm):
    def _get_changed_data(self):
        if self._changed_data is None:
            self._changed_data = self.fields.keys()
        return self._changed_data
    changed_data = property(_get_changed_data)


class ExtensionAdmin(admin.StackedInline):
    form = ExtensionForm
    template = 'pagemagic/extension_inline_admin.html'
    extra = 1
    max_num = 1
